﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Caching;

using ServiceStack.Redis;
using ServiceStack.Redis.Generic;

using Fuse8.DomainFramework.CacheService;
using Fuse8.DomainFramework.Domain;
using Fuse8.DomainFramework.Common;

namespace Fuse8.DomainFramework.RedisCacheService
{
    public class RedisCacheService<TEntity, TKey> : BaseDependencyCacheService<TEntity, TKey>, IDisposable
        where TEntity : class, IEntityObject<TKey>, new()
        where TKey : IComparable<TKey>, IEquatable<TKey> 
    {
        #region Fields

        private IRedisClientsManager _clientManager;
        private IRedisClient _client;
        private bool _isDisposed = false;

        #endregion

        #region Constructor

        public RedisCacheService()
        {
            CacheOptions = new CacheServiceOptions();

            _clientManager = new PooledRedisClientManager();
            _client = _clientManager.GetClient();
        }

        public RedisCacheService(CacheServiceOptions options)
            : this()
        {
            Guard.ArgumentNotNull(options, "options");

            if (options.AbsoluteTimeSpanExpiration != TimeSpan.Zero)
            {
                CacheOptions.AbsoluteTimeSpanExpiration = options.AbsoluteTimeSpanExpiration;
            }

            CacheOptions.UseAsyncMethods = options.UseAsyncMethods;

            if (!string.IsNullOrEmpty(options.KeyPrefix))
            {
                CachePrefix = options.KeyPrefix;
            }
        }

        #endregion

        #region IDisposable Members

        ~RedisCacheService()
        {
            Dispose(false);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!_isDisposed)
            {
                if (disposing)
                {
                    if (_clientManager != null)
                    {
                        _clientManager.Dispose();
                    }
                }
            }

            _isDisposed = true;
        }

        public void Dispose()
        {
            Dispose(true);

            GC.SuppressFinalize(this);
        }

        #endregion

        #region Implemetations

        protected override TStored ClientGet<TStored>(string cacheKey)
        {
            return _client.Get<TStored>(cacheKey);
        }

        protected override IDictionary<string, TStored> ClientGet<TStored>(IEnumerable<string> cacheKey)
        {
            return _client.GetValuesMap<TStored>(cacheKey.ToList());
        }

        protected override bool ClientStore<TStored>(CacheStoreMode mode, string cacheKey, TStored cachedObject, DateTime absoluteExpiration, TimeSpan slidingExpiration)
        {
            bool result = false;

            if (mode == CacheStoreMode.Add)
            {
                if (absoluteExpiration == Cache.NoAbsoluteExpiration)
                {
                    result = _client.Add(cacheKey, cachedObject);
                }
                else if (slidingExpiration == Cache.NoSlidingExpiration)
                {
                    result = _client.Add(cacheKey, cachedObject, absoluteExpiration);
                }
                else
                {
                    result = _client.Add(cacheKey, cachedObject, slidingExpiration);
                }
            }
            else if (mode == CacheStoreMode.Set)
            {
                if (absoluteExpiration == Cache.NoAbsoluteExpiration)
                {
                    result = _client.Set(cacheKey, cachedObject);
                }
                else if (slidingExpiration == Cache.NoSlidingExpiration)
                {
                    result = _client.Set(cacheKey, cachedObject, absoluteExpiration);
                }
                else
                {
                    result = _client.Set(cacheKey, cachedObject, slidingExpiration);
                }
            }
            else if (mode == CacheStoreMode.Replace)
            {
                if (absoluteExpiration == Cache.NoAbsoluteExpiration)
                {
                    result = _client.Replace(cacheKey, cachedObject);
                }
                else if (slidingExpiration == Cache.NoSlidingExpiration)
                {
                    result = _client.Replace(cacheKey, cachedObject, absoluteExpiration);
                }
                else
                {
                    result = _client.Replace(cacheKey, cachedObject, slidingExpiration);
                }
            }

            return result;
        }

        protected override bool ClientRemove<TStored>(string cacheKey)
        {
            return _client.Remove(cacheKey);
        }

        protected override void ClientFlushAll()
        {
            _client.FlushAll();
        }

        public override IEnumerable<CacheItemView> CacheItems
        {
            get
            {
                var items = _client.GetValuesMap(_client.GetAllKeys());

                foreach (var item in items)
                {
                    yield return new CacheItemView
                    {
                        Key = item.Key,
                        Value = item.Value
                    };
                }
            }
        }

        #endregion
    }
}
